from selenium.common.exceptions import NoSuchElementException

from common.decorators import log4gs
from common.functions import get_xpath
from common.functions import has_text_wait


@log4gs
class VueTree(object):
    """
    不带选择框的树，树结构：
    <div class='ivu-tree'>
        <ul class='ivu-tree-children'>
            <li>
                <span class='ivu-tree-arrow'></span>
                <span claas='ivu-tree-title'>浙江省</span>
                <ul>
                    <li class='ivu-tree-children'>
                        <span class='ivu-tree-arrow'></span>
                        <span claas='ivu-tree-title'>杭州市</span>
                        <ul class='ivu-tree-children'>
                            <li>
                                <span class='ivu-tree-arrow'></span>
                                <span claas='ivu-tree-title'>江干区</span>
                                <ul class='ivu-tree-children'>
                                    ...
                                </ul>
                            </li>
                        </ul>
                    </li>
                </ul>
            </li>
        </ul>
    </div>
    """

    treeClassName = 'ivu-tree'
    childrenClassName = 'ivu-tree-children'
    arrowClassName = 'ivu-tree-arrow'
    titleClassName = 'ivu-tree-title'

    targetClassName = titleClassName

    def __init__(self, index=None, describe=None):
        """
        :param index: 目标树在整个页面的序号，查看序号的方式：
            在浏览器中执行document.getElementsByClassName('ivu-tree')

        使用方法：
            tree = VueTree(index=0)   # 当前页第一个树
        """
        if not isinstance(index, int):
            raise ValueError("index要求是整数")
        self.index = index
        self.describe = describe
        ### target_element是最终要点击的目标，为了兼容勾选类型的树。网格树是点击文本，而权限树是点击勾选框
        self.target_element = None

    def __get__(self, instance, owner):
        self.driver = instance.driver
        self.tree = self.driver.find_elements_by_class_name(self.treeClassName)[self.index]
        self.supply = self.tree
        return self

    @property
    def xpath(self):
        try:
            xpath = get_xpath(self.driver, self.tree)
            self.log.info("列表的xpath是：{}".format(xpath))
            return xpath
        except Exception:
            self.log.error("获取列表xpath失败", exc_info=True)

    def __expand_or_select(self, children, type_):
        """展开树或者选最终的节点"""
        length = len(children)
        for i, ul in enumerate(children):
            ### replace('-', '')为了防止实际文本中有-，而影响到node_str的处理逻辑
            self.title_element = ul.find_element_by_class_name(self.titleClassName)
            self.target_element = ul.find_element_by_class_name(self.targetClassName)
            ### has_text_wait等树的子节点加载出来文本再去做操作
            has_text_wait(self.title_element)

            title = self.title_element.text.strip().replace('-', '')
            if title == self.node_str[self.__count]:
                if type_ == 'expand':
                    ### 点击树的展开按钮
                    self.arrow_element = ul.find_element_by_class_name(self.arrowClassName)
                    self.arrow_element.click()
                    self.__count += 1
                    ### 递归调用__select
                    ul_children = ul.find_elements_by_class_name(self.childrenClassName)
                    self.__find(ul_children)
                else:
                    ### 点击目标节点
                    self.target_element.click()
                break
            else:
                if i == length - 1:
                    raise NoSuchElementException("没有找到树节点'{}'".format(self.node_str[self.__count]))

    def __find(self, children):
        """递归选择最后一个数节点"""
        if self.__count < len(self.node_str) - 1:
            self.__expand_or_select(children, 'expand')
        else:
            self.__expand_or_select(children, 'select')

    def select(self, node_str):
        """
        :param node_str: 需要选择的节点路径，节点之间用-连接，单个节点不用-。例如网格选择树：
            多个节点：江西省-南昌市-高新区
            单个节点：江西省
            要求：如果实际节点的文本里面有-，那么就跳过这个符号，比如：江西-省，node_str直接写成江西省

            注：最后只会选择最后一个节点，比如江西省-南昌市-高新区，只会选择高新区
        :return: 返回目标节点，如果需要选择（点击）要用下面的click方法
        """
        if '-' not in node_str:
            self.node_str = [node_str]
        else:
            self.node_str = node_str.split('-')

        self.__count = 0
        ul_children = self.tree.find_elements_by_class_name(self.childrenClassName)
        self.__find(ul_children)


@log4gs
class VueBoxTree(VueTree):
    """带选择框的树"""

    checkBoxClassName = 'ivu-checkbox'

    targetClassName = checkBoxClassName

    def __int__(self, index=None, describe=None):
        super(VueBoxTree, self).__int__(index=index, describe=describe)